package com.gitee.wsl.compose.use.redux

import com.gitee.wsl.base.Tuple2
import com.gitee.wsl.base.Tuple5

import kotlin.reflect.KClass

/**
 * data class Todo(val name: String, val id: String)
 *
 * sealed interface TodoAction
 * data class AddTodo(val todo: Todo) : TodoAction
 * data class DelTodo(val id: String) : TodoAction
 *
 * val todoReducer: Reducer<List<Todo>, TodoAction> = { prevState: List<Todo>, action: TodoAction ->
 *     when (action) {
 *         is AddTodo -> buildList {
 *             addAll(prevState)
 *             add(action.todo)
 *         }
 *         is DelTodo ->  prevState.filter { it.id != action.id }
 *     }
 * }
 *
 * val store = createStore {
 *     todoReducer with emptyList() // 使用中缀函数 with 来连接reducer函数与初始状态
 *     //otherReducer with OtherData("default", 18) //另一个 reducer
 * }
 *
 * @Composable
 * fun UseReduxExample() {
 *     ReduxProvider(store = store) {
 *         Header()
 *         TodoList()
 *
 *     }
 * }
 *
 * @Composable
 * fun TodoList() {
 *     val todos = useSelector<List<Todo>>() //需要传递 状态 的类型
 *     Column {
 *         todos.map {
 *             TodoItem(item = it)
 *         }
 *     }
 * }
 *
 *@Composable
 * fun Header() {
 *     val dispatch = useDispatch<TodoAction>() //需要传递 Action 的类型
 *     val (input, setInput) = useState("")
 *     Row {
 *         OutlinedTextField(
 *             value = input,
 *             onValueChange = setInput,
 *         )
 *         TButton(text = "add") {
 *             dispatch(AddTodo(Todo(input, NanoId.generate())))
 *             setInput("")
 *         }
 *     }
 * }
 *
 *
 * @Composable
 * fun TodoItem(item: Todo) {
 *     val dispatch = useDispatch<TodoAction>()
 *     Row(modifier = Modifier.fillMaxWidth()) {
 *         Text(text = item.name)
 *         TButton(text = "del") {
 *             dispatch(DelTodo(item.id))
 *         }
 *     }
 * }
 *
 */

internal typealias StoreEntry = Tuple5<
        Reducer<Any, Any>, // reducer
        Any, // initialState
        KClass<*>, // state type
        KClass<*>, // action type
        String // alias
        >

/**
 * tuple: reducer \ initialState \ state type \action type\ alias
 */
internal typealias Store =
    Tuple2<
        Array<Middleware<Any, Any>>,
        List<StoreEntry>
        >

class StoreScope private constructor(val list: MutableList<StoreEntry>) {
    inline fun <reified T : Any, reified A : Any> add(
        pair: Pair<Reducer<T, A>, T>,
        alias: String? = null,
    ) {
        list.add(
            Tuple5(
                pair.first as Reducer<Any, Any>,
                pair.second,
                T::class,
                A::class,
                alias ?: T::class.qualifiedName ?: "unknown" // default alias
            )
        )
    }

    inline infix fun <reified T : Any, reified A : Any> Reducer<T, A>.with(state: T) {
        add(Pair(this@with, state))
    }

    object NamedScope {
        inline infix fun <reified T : Any, reified A : Any> Reducer<T, A>.with(state: T): Pair<Reducer<T, A>, T> {
            return Pair(this@with, state)
        }
    }

    inline fun <reified T : Any, reified A : Any> named(
        alias: String,
        fn: NamedScope.() -> Pair<Reducer<T, A>, T>,
    ) {
        add(NamedScope.fn(), alias)
    }

    companion object {
        internal fun getInstance(map: MutableList<StoreEntry>) = StoreScope(map)
    }

    inline fun < reified T : Any,  reified A > reducer(initState:T, noinline action:Reducer<T, A>){
        add(action to initState)
    }
}

/**
 * Create store
 *
 * @param fn
 * @return
 * @receiver
 */
fun createStore(
    middlewares: Array<Middleware<Any, Any>> = emptyArray(),
    fn: StoreScope.() -> Unit,
): Store {
    val list = mutableListOf<StoreEntry>()
    StoreScope.getInstance(list).fn()
    return middlewares to list
}

inline fun <reified T : Any, reified A> createStore(initState:T, noinline action:Reducer<T, A>)= createStore {
    add(action to initState)
}

fun registerErr(): Nothing {
    error("Please confirm that you have correctly registered in `createStore`!")
}

/**
 * Convenience function for creating a [Middleware]
 * usage:
 *    val myMiddleware = middleware { store, next, action -> doStuff() }
 */
/*
fun <S, A> middleware(dispatch: (Store, next: Dispatch<A>, action: A) -> A): Middleware<S, A> =
    { store ->
        { next ->
            { action ->
                dispatch(store, next, action)
            }
        }
    }*/
